2009年03月11日
川俣晶の縁側ソフトウェア技術雑記 total 8595 count

C#において、x++;はx += 1;に置き換えられるが、x = x + 1;に置き換えられるとは限らない

Written By: 川俣 晶連絡先

 ushortを多用したC#ソースをいじっているうちに気付きました。

 C#で記述された以下のソースコード断片があるとします。

ushort x = 0;

x++;

 ここの2行目を以下のように置き換えることができ、同じ結果が得られます。

x += 1;

 しかし、以下のように書き換えるとコンパイルエラーになり、

x = x + 1;

 エラー内容は以下の通りです。

  • エラー 1 型 'int' を 'ushort' に暗黙的に変換できません。明示的な変換が存在します。(cast が不足していないかどうかを確認してください)

 intよりも小さいビットサイズの型を使うと、たぶんどれでも同じような現象が起きるだろうと思います。intならx = x + 1;への置き換えも上手く行きます。

WHY? §

 あらためて調べてみると興味深いことに、x++;とx += 1;とx = x + 1;の挙動の根拠は全て違います。

 まず、x = x + 1;についてはC#には16bit/8bitの加算が存在しない問題で述べたように、ushortの加算が存在しないため"x + 1"の型はintになり、intからushortへの暗黙変換が存在しないためにエラーになります。

 次に、x += 1;ですが、これは複合代入に対する以下のルールによって記述可能になっています。つまり暗黙的にキャストが補われています。

• 選択された演算子が定義済みの演算子で、選択された演算子の戻り値の型を x の型に "明示的に" 変換でき、y を x の型に "暗黙に" 変換できる場合または演算子がシフト演算子の場合、演算は x = (T)(x op y) として評価されます。ただし、T は x の型で、x は 1 回だけ評価されます。

 最後にx++;ですが、これは以下のように"+"よりも多くの型に対する演算子が用意されているために、記述可能となっています。

sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal の各型およびすべての列挙型には、定義済みの ++ 演算子と -- 演算子があります。

 ちなみに、生成したバイナリを逆アセンブルして見たところ、どれも同じでした。定義済のushort型++演算子があっても、ILレベルに落ちるときにはやはり32bit加算を経由する、ということなのでしょう。たぶん。

感想 §

 ここ数年、毎日のようにC#のコードを書いていましたが、こういう基本的な部分にこのような問題があったとは。かなり新鮮です。